/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef MC_TEST_HI182X_COMMON_H
#define MC_TEST_HI182X_COMMON_H

#include <devmon/driver_abi.h>
#include <dlfcn.h>
#include <functional>
#include <gtest/gtest.h>
#include <test_utilities/test_base.h>
#include <mc/common.h>
#include <ncsi/ncsi_socket.h>
#include <ncsi/ncsi_protocol.h>
#include <ncsi/ncsi_huawei.h>
#include <ncsi_over_mctp/ncsi_over_mctp_huawei.h>
#include <mctp/mctp.h>
#include <imu/imu.h>
#include <string>
#include <vector>
#include <cstring>
#include <thread>
#include <chrono>

#include "hi182x/hi182x_card.h"
#include "hi182x/hi182x_om.h"
#include "hi182x/hi182x_port.h"

// 声明外部函数
namespace dev {
void set_mctp_request_handler(std::function<bool(const mc::dict&, const std::vector<uint8_t>&,
                                                  std::string&)> handler);
} // namespace dev

using namespace dev;

namespace drivers {
namespace pcie_nic_card {
namespace hisi {
namespace test {

    
// 辅助函数：字节序转换
constexpr uint16_t host_to_be16(uint16_t value) {
    return static_cast<uint16_t>(((value & 0x00FFU) << 8U) | ((value & 0xFF00U) >> 8U));
}

// Mock smbus - 由于send_request不是虚函数，我们override send_and_receive方法
// send_request最终会调用send_and_receive，而send_and_receive是虚函数
// dev::smbus使用CRC32协议，响应格式：error_code(2) + opcode(2) + total_length(4) + length(4) + data(N) + CRC32(4)
class mock_smbus : public dev::smbus {
public:
    mock_smbus() : dev::smbus(nullptr) {
    }

    void set_send_request_result(const std::string& data_result) {
        m_data_result = data_result;
    }

    void set_send_request_result_for_opcode(uint16_t opcode, const std::string& data_result) {
        m_opcode_results[opcode] = data_result;
    }

    std::pair<bool, std::vector<uint8_t>> send_and_receive(const std::string& data,
                                                           uint32_t len) override {
        // 从请求数据中解析 opcode
        // dev::smbus 请求格式：reserved(2) + opcode(2) + offset(4) + length(4)
        uint16_t request_opcode = 0x05;  // 默认值
        if (data.length() >= 4) {
            // opcode 在 reserved (2 bytes) 之后
            request_opcode = static_cast<uint8_t>(data[2]) | (static_cast<uint8_t>(data[3]) << 8);
        }

        // 根据 opcode 获取对应的响应数据
        std::string response_data = m_data_result;
        if (m_opcode_results.find(request_opcode) != m_opcode_results.end()) {
            response_data = m_opcode_results[request_opcode];
        }

        // 如果数据为空，返回空响应
        if (response_data.empty()) {
            return std::make_pair(false, std::vector<uint8_t>());
        }
        
        // 构建完整的SMBus响应格式（dev::smbus使用CRC32）
        // error_code (2 bytes) - 0x0000表示成功
        // opcode (2 bytes) - 从请求中获取
        // total_length (4 bytes) - 数据总长度
        // length (4 bytes) - 当前帧数据长度
        // data (N bytes) - 实际数据
        // CRC32 (4 bytes) - 校验码
        
        uint32_t data_len = static_cast<uint32_t>(response_data.length());
        
        std::vector<uint8_t> response;
        response.reserve(12 + data_len + 4); // header(12) + data + CRC32(4)
        
        // error_code (2 bytes) - 0x0000表示成功
        response.push_back(0x00);
        response.push_back(0x00);
        
        // opcode (2 bytes) - 从请求中获取
        response.push_back(static_cast<uint8_t>(request_opcode & 0xFF));
        response.push_back(static_cast<uint8_t>((request_opcode >> 8) & 0xFF));
        
        // total_length (4 bytes) - 数据总长度
        response.push_back(static_cast<uint8_t>(data_len & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 8) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 16) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 24) & 0xFF));
        
        // length (4 bytes) - 当前帧数据长度
        response.push_back(static_cast<uint8_t>(data_len & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 8) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 16) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 24) & 0xFF));
        
        // data (N bytes) - 实际数据
        response.insert(response.end(), response_data.begin(), response_data.end());
        
        // CRC32 (4 bytes) - 计算CRC32校验码
        // CRC32计算需要：response数据(不含CRC32)
        std::string bin_data(reinterpret_cast<const char*>(response.data()), response.size());
        uint32_t crc32 = mc::crypto::CRC32().calculate(bin_data, 0, true);
        // 将CRC32转换为4字节（little-endian）
        response.push_back(static_cast<uint8_t>(crc32 & 0xFF));
        response.push_back(static_cast<uint8_t>((crc32 >> 8) & 0xFF));
        response.push_back(static_cast<uint8_t>((crc32 >> 16) & 0xFF));
        response.push_back(static_cast<uint8_t>((crc32 >> 24) & 0xFF));
        
        return std::make_pair(true, response);
    }

private:
    std::string m_data_result;
    std::map<uint16_t, std::string> m_opcode_results;
};
class test_interface : public mc::engine::interface<test_interface> {
    public:
        MC_INTERFACE("bmc.kepler.IpmiCore")
    
        //a{ss}yyyyyay
        mc::variant Request(
            const mc::dict& ctx,              // a{ss} - 上下文字典
            uint8_t channel_type,              // y - 通道类型
            uint8_t instance,                  // y - 实例序号
            uint8_t net_fn,                   // y - 网络功能码
            uint8_t lun,                      // y - 逻辑单元号
            uint8_t cmd,                      // y - 命令字
            const std::vector<uint8_t>& data  // ay - 命令参数数据
        ) {
            ilog("test_ipmb_interface: Request");
            mc::variants result = {mc::variant(0), mc::variant(
                std::vector<uint8_t>{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F})};
            return mc::variant(result);
        }
    };
    
    class test_object : public mc::engine::object<test_object> {
    public:
        MC_OBJECT(test_object, "bmc.kepler.IpmiCore", "/bmc/kepler/IpmiCore", (test_interface))
    
        test_interface m_test_interface;
    };
// 测试服务类
class hi182x_test_service : public mc::engine::service {
public:
    hi182x_test_service(const std::string& name) : mc::engine::service(name) {}
};

// 公共测试基类
class Hi182xAbiTest : public mc::test::TestWithEngine {
public:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service.init();
        m_test_service.start(); 
        auto m_obj = test_object::create();
        m_obj->set_object_path("/bmc/kepler/IpmiCore");
        m_obj->set_object_name("bmc.kepler.IpmiCore");
        m_test_service.register_object(m_obj);
        m_mctp = mc::make_shared<dev::mctp>(nullptr, 0, dev::MCTP_MESSAGE_TYPE_NCSI, "");
        m_smbus = mc::make_shared<mock_smbus>();
        // m_ncsi = std::make_shared<ncsi_over_mctp_huawei>(m_mctp.get());
        load_driver();
    }

    static void TearDownTestSuite() {
        m_test_service.stop();
        if (m_driver_handle != nullptr) {
            dlclose(m_driver_handle);
            m_driver_handle = nullptr;
        }
        TestWithEngine::TearDownTestSuite();
    }

    void SetUp() override {}

    void TearDown() override {}

    static void load_driver() {
        m_driver_handle = dlopen("./opt/bmc/drivers/libhisi_182x.so", RTLD_LAZY);
        if (m_driver_handle == nullptr) {
            MC_THROW(mc::system_exception, "Failed to load driver: ${error}", ("error", dlerror()));
        }
        register_device_driver_func func = reinterpret_cast<register_device_driver_func>(
            dlsym(m_driver_handle, "register_device_driver"));
        MC_ASSERT_THROW(func, mc::bad_function_call_exception,
                        "Failed to get export_device_driver function: ${error}",
                        ("error", dlerror()));

        device_driver_t* devices       = nullptr;
        uint8_t          devices_count = 0;
        status_t         ret           = func(&devices, &devices_count);
        MC_ASSERT_THROW(ret == STATUS_OK, mc::bad_function_call_exception,
                        "Failed to get device driver manager: ${error}", ("error", ret));

        m_devices.clear();
        m_devices.reserve(devices_count);
        for (uint8_t i = 0; i < devices_count; i++) {
            auto device_driver = std::make_shared<device_driver_t>(devices[i]);
            m_devices.emplace(device_driver->device_name, device_driver);
        }
    }

    mc::dict create_test_connector(uint8_t system_id, uint8_t position, uint8_t slot) {
        return mc::dict{{"SystemId", system_id}, {"Position", position}, {"Slot", slot}};
    }

    static hi182x_test_service                                               m_test_service;
    static std::unordered_map<std::string, std::shared_ptr<device_driver_t>> m_devices;
    static void*                                                             m_driver_handle;
    static mc::shared_ptr<mock_smbus>                                        m_smbus;
    static ncsi_over_mctp_hw_ptr                                            m_ncsi;
    static mc::shared_ptr<dev::mctp>                                        m_mctp;
};

// 静态成员初始化
inline hi182x_test_service Hi182xAbiTest::m_test_service{"bmc.kepler.ipmi_core"};
inline std::unordered_map<std::string, std::shared_ptr<device_driver_t>> Hi182xAbiTest::m_devices;
inline void* Hi182xAbiTest::m_driver_handle = nullptr;
inline mc::shared_ptr<mock_smbus> Hi182xAbiTest::m_smbus = nullptr;
inline ncsi_over_mctp_hw_ptr Hi182xAbiTest::m_ncsi = nullptr;
inline mc::shared_ptr<dev::mctp> Hi182xAbiTest::m_mctp = nullptr;

} // namespace test
} // namespace hisi
} // namespace pcie_nic_card
} // namespace drivers

#endif // MC_TEST_HI182X_COMMON_H

